#include "UIFile.h"
#include "StretchyLayout.h"

#include <fstream>
#include <sstream>
#include <cstdlib>
#include <cstdio>
#include <stack>

namespace Lame3d
{
	// UIFile :: Entry
	UIFile::Entry::Entry(const std::string &line)
	{
		unsigned int spaceIdx;
		spaceIdx = line.find(" ");
		if(spaceIdx == std::string::npos || spaceIdx == line.size() - 1) {
			fprintf(stderr, "Malformed UIFile line: %s !!\n", line.c_str());
			throw "Malformed UIFile line.";
		}
		key = line.substr(0, spaceIdx);
		value = line.substr(spaceIdx+1);
	}
	
	UIFile::Entry::~Entry()
	{
		
	}
	
	UIFile::Entry& UIFile::Entry::operator=(const std::string &v)
	{
		value = v;
		return *this;
	}
	
	UIFile::Entry& UIFile::Entry::operator=(float f)
	{
		std::stringstream ss;
		ss << f;
		ss >> value;
		return *this;
	}
	
	UIFile::Entry& UIFile::Entry::operator=(int i)
	{
		std::stringstream ss;
		ss << i;
		ss >> value;
		return *this;
	}
	
	const std::string& UIFile::Entry::String() const
	{
		return value;
	}
	
	int UIFile::Entry::Int() const
	{
		return atoi(value.c_str());
	}
	
	float UIFile::Entry::Float() const
	{
		return atof(value.c_str());
	}
	
	const std::string& UIFile::Entry::Key() const
	{
		return key;
	}
	
	const std::string& UIFile::Entry::Value() const
	{
		return value;
	}
	
	// UIFile :: UIFile
	UIFile::UIFile(const File &f)
			: rootNode(NULL), file(f)
	{
		buildTree();
	}
	
	UIFile::~UIFile()
	{
		delete rootNode;
	}
	
	Window* UIFile::GenerateWindow() const
	{
		Window *nWindow = NULL;		
		Point2d windowSize(
			rootNode->GetEntry("width")->Float(),
			rootNode->GetEntry("height")->Float() );
		Point2d windowLoc(
			rootNode->GetEntry("x")->Float(),
			rootNode->GetEntry("y")->Float() );
		
		// Check for layouts.
		const std::string& layout = rootNode->GetEntry("layout")->String();  
		if(layout == "absolute") {
			// ***************
			// ABSOLUTE LAYOUT 
			// ***************
			AbsoluteLayout *layout = new AbsoluteLayout();
			nWindow = new Window(
				rootNode->Name(),
				windowSize, windowLoc,
				layout);
				
			std::map<std::string, ParseNode*>::const_iterator it;
			ParseNode* cNode;
			for(it = rootNode->NodesBegin(); it != rootNode->NodesEnd(); ++it) {
				cNode = it->second;
				
				if(cNode->Type() == "label") {
					// Label
					UILabel *label = NULL;
					label = cNode->BuildLabel();
					nWindow->Add(label, NULL);
				} else if(cNode->Type() == "button") {
					// Button
					UIButton *button = NULL;
					button = cNode->BuildButton();
					nWindow->Add(button, NULL);
				} else if(cNode->Type() ==  "textentry") {
					UITextEntry *entry = cNode->BuildTextEntry();
					nWindow->Add(entry, NULL);
				} else {
					throw "Did not understand UIFile component. (AbsoluteLayout)";
				}
			}
		} else if(layout == "stretchy") {
			// ***************
			// STRETCHY LAYOUT
			// ***************
			StretchyLayout *layout = new StretchyLayout();
			nWindow = new Window(
				rootNode->Name(),
				windowSize, windowLoc,
				layout);
				
			std::map<std::string, ParseNode*>::const_iterator it;
			ParseNode *cNode;
			for(it = rootNode->NodesBegin(); it != rootNode->NodesEnd(); ++it) {
				cNode = it->second;
				UILayoutProperties *props = cNode->BuildStretchyProps();				
				
				if(cNode->Type() == "label") {
					// label
					UILabel *label = cNode->BuildLabel();
					nWindow->Add(label, props);
				} else if(cNode->Type() == "button") {
					// button
					UIButton *button = cNode->BuildButton();
					nWindow->Add(button, props);
				} else if(cNode->Type() == "textentry") {
					// text entry
					UITextEntry *entry = cNode->BuildTextEntry();
					nWindow->Add(entry, props);
				} else {
					throw "Didn't understand UIFile component. (StretchyLayout)";
				}
			}
			
			layout->Pack();
			
		} else {
			delete nWindow;
			throw "Did not understand UIFile layout.";
		}
		
		// set resizability
		nWindow->Resizable(
			rootNode->GetEntry("resizable")->Int() == 1);
		
		return nWindow;
	}
	
	void UIFile::buildTree()
	{
		std::ifstream input(file.GetPath().c_str());
		printf("Loading UIFile at %s...\n", file.GetPath().c_str()); 
		int nTabs = 0;
		std::string line;
		
		// get root node.
		getline(input, line);
		nTabs = numTabs(line);
		if(nTabs != 0) {
			input.close();
			throw "Malformed UIFile at line 0 - expected no tabs!";
		}
		rootNode = new ParseNode(line);
		if(rootNode->type != "window") {
			throw "Illegal root type -- expected 'window'";
		}
		
		// get everything else
		std::stack<ParseNode*> parseNodeStack;
		parseNodeStack.push(rootNode);
		getline(input, line);
		int lineNum = 2;
		while(input) {
			printf("Looking at line: %s ... [%d]\n", line.c_str(), lineNum++);
			int newNumTabs = numTabs(line);
			line = line.substr(newNumTabs);
			if(newNumTabs % 2 == 1) {
				// property
				UIFile::Entry* newEnt = new UIFile::Entry(line);
				parseNodeStack.top()->entries[newEnt->key] = newEnt;
			} else {
				// parsenode
				int diff = (newNumTabs - nTabs)/2;
				for(; diff <= 0; ++diff) {
					parseNodeStack.pop();
				}
				UIFile::ParseNode* pNode = new UIFile::ParseNode(line);
				parseNodeStack.top()->subNodes[pNode->name] = pNode;
				parseNodeStack.push(pNode);
				nTabs = newNumTabs;
			}
			getline(input, line);
		}
		
		input.close();
	}
	
	int UIFile::numTabs(const std::string &s)
	{
		int i;
		int size = s.size();
		for(i=0; size && (s[i] == '\t'); ++i) {	}
		return i;
	}
	
	// UIFile :: ParseNode //
	UIFile::ParseNode::ParseNode(const std::string& line)
	{
		unsigned int spaceIdx;
		spaceIdx = line.find(" ");
		if(spaceIdx == std::string::npos || spaceIdx == line.size() - 1) {
			fprintf(stderr, "Malformed UIFile line: %s !!\n", line.c_str());
			throw "Malformed UIFile line.";
		}
		type = line.substr(0, spaceIdx);
		name = line.substr(spaceIdx+1);
	}
	
	UIFile::ParseNode::~ParseNode()
	{
		std::map<std::string, Entry*>::const_iterator entryIt;
		std::map<std::string, ParseNode*>::const_iterator nodeIt;
		
		for(entryIt = EntriesBegin(); entryIt != EntriesEnd(); ++entryIt) {
			delete entryIt->second;
		}
		for(nodeIt = NodesBegin(); nodeIt != NodesEnd(); ++nodeIt) {
			delete nodeIt->second;
		}
	}
	
	const UIFile::Entry* UIFile::ParseNode::GetEntry(const std::string &s) const
	{
		std::map<std::string, Entry*>::const_iterator it;
		it = entries.find(s);
		if(it == entries.end()) {
			return NULL;
		} else {
			return it->second;
		}
	}
	
	std::map<std::string, UIFile::Entry*>::const_iterator UIFile::ParseNode::EntriesBegin() const
	{
		return entries.begin();
	}
	
	std::map<std::string, UIFile::Entry*>::const_iterator UIFile::ParseNode::EntriesEnd() const
	{
		return entries.end();
	}
	
	const UIFile::ParseNode* UIFile::ParseNode::GetNode(const std::string &s) const
	{
		std::map<std::string, ParseNode*>::const_iterator it;
		it = subNodes.find(s);
		if(it == subNodes.end()) {
			return NULL;
		} else {
			return it->second;
		}
	}
	
	std::map<std::string, UIFile::ParseNode*>::const_iterator UIFile::ParseNode::NodesBegin() const
	{
		return subNodes.begin();
	}
	
	std::map<std::string, UIFile::ParseNode*>::const_iterator UIFile::ParseNode::NodesEnd() const
	{
		return subNodes.end();
	}
	
	const std::string& UIFile::ParseNode::Type() const
	{
		return type;
	}
	
	const std::string& UIFile::ParseNode::Name() const
	{
		return name;
	}
	
	UILabel* UIFile::ParseNode::BuildLabel()
	{
		UILabel *label = NULL;
		Point2d loc(
			entries["x"]->Float(),
			entries["y"]->Float() );
		Point2d size(
			entries["width"]->Float(),
			entries["height"]->Float() );
		label = new UILabel(
			name,
			loc,
			size,
			entries["text"]->String(),
			(entries["wrap"]->Int() == 1));
		label->Wrap(entries["wrap"]->Int() == 1);
		printf("wrap from file: %d ... wrappiness %d \n", entries["wrap"]->Int(), label->Wrap());
		if(label->Wrap()) {
			printf("Should be wrapping...\n");
		} 
		const std::string &styleStr = entries["style"]->String();
		if(styleStr == "dark") {
			label->Style(UILabel::DARK);
		} else if(styleStr == "light") {
			label->Style(UILabel::LIGHT);
		}
		buildEvents(label);
		return label;
	}
	
	UIButton* UIFile::ParseNode::BuildButton()
	{
		UIButton *button = NULL;
		Point2d loc(
			entries["x"]->Float(),
			entries["y"]->Float() );
		Point2d size(
			entries["width"]->Float(),
			entries["height"]->Float() );
		button = new UIButton(
			name,
			loc,
			size,
			entries["text"]->String() );
		buildEvents(button);
		return button;
	}
	
	UITextEntry* UIFile::ParseNode::BuildTextEntry()
	{
		UITextEntry *entry = NULL;
		Point2d loc(
			entries["x"]->Float(),
			entries["y"]->Float() );
		Point2d size(
			entries["width"]->Float(),
			entries["height"]->Float() );
		entry = new UITextEntry(
			name,
			loc,
			size );
		buildEvents(entry);
		
		// check for enter event binding
		Entry *n = entries["entermessage"];
		if(n) {
			entry->EnterMessage( n->String() );
		}
		
		return entry;
	}
	
	StretchyLayout::Properties* UIFile::ParseNode::BuildStretchyProps()
	{
		Point2d uL(
			entries["x"]->Float(),
			entries["y"]->Float() );
		Point2d lR(
			entries["x2"]->Float(),
			entries["y2"]->Float() );
		return new StretchyLayout::Properties( uL, lR );
	}
	
	void UIFile::ParseNode::buildEvents(UIElement *e)
	{
		Entry *n = entries["clickedmessage"];
		if(n) {
			e->ClickedMessage( n->String() );
		}
	}
}

// eof //
